#include "../config.h"

#include <gtk/gtk.h>
#include <gtkmm.h>
#include <glibmm/i18n.h>

#include "log.h"
#include "strfuncs.h"
#include "keybind.h"
#include "conf.h"
#include "icon.h"
#include "menu.h"
#include "widgets.h"

namespace gpyp {

//========================
// ConfMain::Tooltip
ConfMain::Tooltip::Tooltip() :
  conf::Element    ("tooltip"),
  color_bg_        ("color_bg",     "#ffffe0"),
  color_fg_        ("color_fg",     "#000000"),
  color_fr_        ("color_fr",     "#000000"),
  color_bg_sel_    ("color_bg_sel", "#E0E6FF"),
  color_fg_sel_    ("color_fg_sel", "#000000"),
  color_fr_sel_    ("color_fr_sel", "#000000"),
  enable_frame_    ("enable_frame",      true),
  enable_frame_sel_("enable_frame_sel",  true),
  xpad_            ("xpad",                -1),
  ypad_            ("ypad",                -1)
{
  add(color_bg_);
  add(color_fg_);
  add(color_fr_);
  add(color_bg_sel_);
  add(color_fg_sel_);
  add(color_fr_sel_);
  add(enable_frame_);
  add(enable_frame_sel_);
  add(xpad_);
  add(ypad_);
}

//========================
// ConfMain::General
ConfMain::General::General() :
  conf::Element     ("general"),
  run_peercast_     ("run_peercast",    false),
  run_pcraw_proxy_  ("run_pcraw_proxy", false),
  avoid_close_      ("avoid_close",     false),
  statusbar_format_ ("statusbar_format",
                     "[%{YP.chcount_valid}/%{YP.chcount}]%{Channel.ypname}:"
                     "([%{Channel.genre}]%{Channel.desc} - "
                     "%{Channel.comment} - %{Channel.track_artist} - "
                     "%{Channel.track_title} - %{Channel.track_album})"),
  shell_            ("shell",             "/bin/sh -c"),
  shell_esc_        ("shell_esc",         "\"$\\`"),
  command_prefix_   ("command_prefix",    "exec "),
  color_positive_   ("color_positive",    "#3465A4"),
  color_negative_   ("color_negative",    "#CC0000"),
  color_unimportant_("color_unimportant", "#888A85"),
  font_monospace_   ("font_monospace",    "monospace 10"),
  tooltip_          (new Tooltip())
{
  add(run_peercast_);
  add(run_pcraw_proxy_);
  add(avoid_close_);
  add(statusbar_format_);
  add(shell_);
  add(shell_esc_);
  add(command_prefix_);

  add(color_positive_);
  add(color_negative_);
  add(color_unimportant_);

  add(font_monospace_);
  add(tooltip_);
}

//========================
// ConfMain::ScreenSaver
ConfMain::ScreenSaver::ScreenSaver() :
  conf::Element     ("screensaver"),
  disable_auto_play_("disable_auto_play", true),
  mute_             ("mute",              true)
{
  add(disable_auto_play_);
  add(mute_);
}

//========================
// ConfMain::Network
ConfMain::Network::Network() :
  conf::Element         ("network"),
  peercast_server_      (new AttributeHost("peercast_server", "localhost", 7144)),
  peercast_server_id_   ("peercast_server_id", ""),
  peercast_server_pw_   ("peercast_server_pw", ""),
  pcraw_proxy_server_   (new AttributeHost("pcraw_proxy_server", "localhost", 8080)),
  useragent_            ("useragent", "%{APP_NAME}/%{APP_VERSION} (%{INFO})"),
  timeout_              ("timeout",             10),
  disable_ipv6_         ("disable_ipv6",     false),
  autoretry_limit_      ("autoretry_limit",      2),
  autoretry_interval_   ("autoretry_interval",  60)
{
  add(peercast_server_);
  add(peercast_server_id_);
  add(peercast_server_pw_);
  add(pcraw_proxy_server_);
  add(useragent_);
  add(timeout_);
  add(disable_ipv6_);
  add(autoretry_limit_);
  add(autoretry_interval_);
}

//========================
// ConfMain::PlayerCollection
const ConfMain::PlayerCollection::Preset
ConfMain::PlayerCollection::PRESETS[] = {
  {"SMPlayer",      "smplayer \"#{Channel.streamurl}\"",                   "smplayer"},
  {"Totem",         "totem \"#{Channel.streamurl}\"",                      "totem"},
  {"VLC",           "vlc \"#{Channel.streamurl|mmsh}\"",                   "vlc"},
  {"GNOME MPlayer", "gnome-mplayer --playlist \"#{Channel.playlisturl}\"", "gnome-mplayer"},
  {"MPlayer",       "mplayer -loop 0 \"#{Channel.streamurl}\"",            "mplayer"},
  {NULL, NULL, NULL}
};

ConfMain::RefPlayer
ConfMain::PlayerCollection::on_create_item(const Glib::ustring &name) {
  return RefPlayer(new Player(name));
}
void ConfMain::PlayerCollection::on_load_default() {
  conf::Collection<Player, PlayerCollection>::on_load_default();
  /* プレイヤー2が設定されててプレイヤー1が空の場合は考慮しなくていいよね */
  Glib::ustring name;
  if (old_play_.empty()) {
    for(const Preset *p = PRESETS; p->name; ++p) {
      add_item(p->name, p->command, p->icon);
    }
  } else {
    name = shell_command_get_name(old_play_);
    add_item(name, old_play_, name);
    if (!old_play2_.empty()) {
      name = shell_command_get_name(old_play2_);
      add_item(name, old_play2_, name);
    }
  }
  if (!old_auto_play_.empty()) {
    name = shell_command_get_name(old_auto_play_);
    add_item(_("Auto play"), old_auto_play_, name)->auto_play() = true;
  }
}
void ConfMain::PlayerCollection::on_default_loaded() {
  if (old_play_.empty() && old_auto_play_.empty()) {
    conf::Collection<Player, PlayerCollection>::on_default_loaded();
  }
}

void ConfMain::PlayerCollection::set_old_play(const Glib::ustring &play) {
  old_play_ = play;
}
void ConfMain::PlayerCollection::set_old_play2(const Glib::ustring &play2) {
  old_play2_ = play2;
}
void ConfMain::PlayerCollection::set_old_auto_play(const Glib::ustring &auto_play) {
  old_auto_play_ = auto_play;
}

ConfMain::PlayerCollection::PlayerCollection() :
  conf::Collection<Player, PlayerCollection>("players", "player")
{
}

//========================
// ConfMain::Command
ConfMain::Command::Command(const Glib::ustring &name,
                           const Glib::ustring &command) :
  conf::Element(name),
  command_     ("command", command)
{
  add(command_);
}

//========================
// ConfMain::BuiltInCommand
const char *ConfMain::BuiltInCommand::PRESET_PEERCAST[] = {"peercast", NULL};
const char *ConfMain::BuiltInCommand::PRESET_PCRAW_PROXY[] = {
  "pcraw_proxy \"#{Network.pcrawsrv_port}\" \"#{Network.peercastsrv}\"",
  NULL
};
const char *ConfMain::BuiltInCommand::PRESET_TERMINAL[] = {
  "gnome-terminal -x %{command}",
  "roxterm -e %{command}",
  NULL
};
const char *ConfMain::BuiltInCommand::PRESET_WEBBROWSER[] = {
#ifdef __APPLE__
  "osascript \"#{Path.Gpyp.applescript}/safari.scpt\" \"#{url}\"",
#endif
  "firefox \"#{url}\"",
  "firefox -new-tab \"#{url}\"",
  "opera \"#{url}\"",
  "opera -newpage \"#{url}\"",
  "chromium-browser \"#{url}\"",
  "firefox-3.5 \"#{url}\"",
  "firefox-3.5 -new-tab \"#{url}\"",
  NULL
};
const char *ConfMain::BuiltInCommand::PRESET_BBSBROWSER[] = {
  "v2c \"#{url}\"",
  "jd \"#{url}\"",
  "@{webbrowser} \"http://localhost/rep2/?url=#{url}\"",
  NULL
};

void ConfMain::BuiltInCommand::on_start_element(conf::ConfParser &parser,
                                                const char       *element_name,
                                                const char      **attribute_names,
                                                const char      **attribute_values,
                                                GError          **error) {
  if (get_element_depth() == 0) {
    if (parser.get_document().version() < "3.3") {
      const char **name = attribute_names, **value = attribute_values;
      for(; *name && *value; ++name, ++value) {
        if (strcmp(*name, "pid") == 0) {
          pid_t pid = touint(*value, 0);
          if (pid) save_pid(dir_path_, element_name, pid);
          break;
        }
      }
    }
    if (parser.get_document().version() < "3.8") {
#define OLD_PLAY(NAME) if (strcmp(element_name, #NAME) == 0) {          \
        const char **name = attribute_names, **value = attribute_values; \
        for(; *name && *value; ++name, ++value) {                       \
          if (strcmp(*name, "command") == 0) {                          \
            players_->set_old_##NAME(*value);                           \
          }                                                             \
        }                                                               \
      }
      OLD_PLAY(play) else OLD_PLAY(play2) else OLD_PLAY(auto_play);
#undef OLD_PLAY
    }
  }
  conf::Element::on_start_element(parser, element_name,
                                  attribute_names, attribute_values,
                                  error);
}

ConfMain::BuiltInCommand::BuiltInCommand(const Glib::ustring &dir_path) :
  conf::Element("command"),
  dir_path_    (dir_path),
  peercast_    ("peercast",    PRESET_PEERCAST[0]),
  pcraw_proxy_ ("pcraw_proxy", PRESET_PCRAW_PROXY[0]),
  terminal_    ("terminal",    PRESET_TERMINAL[0]),
  webbrowser_  ("webbrowser",  PRESET_WEBBROWSER[0]),
  bbsbrowser_  ("bbsbrowser",  PRESET_BBSBROWSER[0]),
  players_     (new PlayerCollection())
{
  add(peercast_);
  add(pcraw_proxy_);
  add(terminal_);
  add(webbrowser_);
  add(bbsbrowser_);

  add(players_);
}

//========================
// ConfMain::Record
const char *ConfMain::Record::PRESETS[] = {
  "mencoder -quiet -nocache -ovc copy -oac copy -noidx \"#{Channel.streamurl}\" -o \"#{filename}\"",
  "mplayer -dumpstream -dumpfile \"#{filename}\" \"#{Channel.streamurl}\"",
  NULL
};

ConfMain::Record::Record() :
  conf::Element   ("record"),
  command_        ("command", PRESETS[0]),
  filename_       ("filename", "%{Path.home}/peercast/%{Channel.name}/%{date|%Y%m%d}.avi"),
  retry_timeout_  ("retry_timeout",      30),
  retry_count_    ("retry_count",         5),
  retry_ch_exists_("retry_ch_exists", false),
  rm_zero_file_   ("rm_zero_file",     true),
  term_on_exit_   ("term_on_exit",     true),
  kill_on_exit_   ("kill_on_exit",    false),

  save_log_       ("log_save",         true),
  log_filename_   ("log_filename", "%{dir}/%{date|%Y%m%d_%H%M%S}.log")
{
  add(command_);
  add(filename_);
  add(retry_timeout_);
  add(retry_count_);
  add(retry_ch_exists_);
  add(rm_zero_file_);
  add(term_on_exit_);
  add(kill_on_exit_);

  add(save_log_);
  add(log_filename_);
}

//========================
// ConfMain::PresetColor
ConfMain::PresetColor::PresetColor(const Glib::ustring &name) :
  conf::Element(name),
  color_       ("color", Color())
{
  add(color_);
}

//========================
// ConfMain::PresetColors
void ConfMain::PresetColors::on_load_default() {
  conf::Collection<PresetColor, PresetColors>::on_load_default();
  // Butter
  add_item("#ebdf88");
  add_item("#fce94f");
  add_item("#edd400");
  add_item("#c4a000");
  // Orange
  add_item("#fcaf3e");
  add_item("#f57900");
  add_item("#ce5c00");
  // Chocolate
  add_item("#e9b96e");
  add_item("#c17d11");
  add_item("#8f5902");
  // Chameleon
  add_item("#bcff7a");
  add_item("#8ae234");
  add_item("#73d216");
  add_item("#4e9a06");
  // Sky Blue
  add_item("#add5ff");
  add_item("#729fcf");
  add_item("#3465a4");
  add_item("#204a87");
  // Plum
  add_item("#ebb2e4");
  add_item("#ad7fa8");
  add_item("#75507b");
  add_item("#5c3566");
  // Scarlet Red
  add_item("#ffa3a3");
  add_item("#ef2929");
  add_item("#cc0000");
  add_item("#a40000");

  // Aluminium
  add_item("#ffffff");
  add_item("#eeeeec");
  add_item("#d3d7cf");
  add_item("#babdb6");
  add_item("#888a85");
  add_item("#555753");
  add_item("#2e3436");
  add_item("#000000");

  // YP white
  add_item("#e2f5e2"); // green  / CP
  add_item("#e2e9f5"); // blue   / XP
  add_item("#f5f5e2"); // yellow / TP
  add_item("#f5e2f5"); // purple / RP
  add_item("#f5e2e2"); // red    / SP
  // YP dark
  add_item("#333d33"); // green  / CP
  add_item("#33373d"); // blue   / XP
  add_item("#3d3d33"); // yellow / TP
  add_item("#3d333d"); // purple / RP
  add_item("#3d3333"); // red    / SP
}
ConfMain::RefPresetColor ConfMain::PresetColors::on_create_item(const Glib::ustring &name) {
  return RefPresetColor(new PresetColor(name));
}

ConfMain::RefPresetColor ConfMain::PresetColors::add_item(const Color &color) {
  RefPresetColor item = conf::Collection<PresetColor, PresetColors>::add_item();
  item->color() = color;
  return item;
}
ConfMain::RefPresetColor ConfMain::PresetColors::add_item(const Glib::ustring &color) {
  return add_item(Color(color));
}

ConfMain::PresetColors::PresetColors(const Glib::ustring &name) :
  conf::Collection<PresetColor, PresetColors>(name, "item")
{
}

//========================
// ConfMain::Terminal
ConfMain::Terminal::Terminal() :
  conf::Element("terminal"),
  columns_     ("columns", 50),
  rows_        ("rows",    10),

  color_foreground_("color_foreground", "black"),
  color_background_("color_background", "white"),
  color_cursor_    ("color_cursor",     "black"),
  color_highlight_ ("color_highlight"),

  color_palette1_ ("color_palette1",  "#2E3436"),
  color_palette2_ ("color_palette2",  "#CC0000"),
  color_palette3_ ("color_palette3",  "#4E9A06"),
  color_palette4_ ("color_palette4",  "#C4A000"),
  color_palette5_ ("color_palette5",  "#3465A4"),
  color_palette6_ ("color_palette6",  "#75507B"),
  color_palette7_ ("color_palette7",  "#06989A"),
  color_palette8_ ("color_palette8",  "#D3D7CF"),
  color_palette9_ ("color_palette9",  "#555753"),
  color_palette10_("color_palette10", "#EF2929"),
  color_palette11_("color_palette11", "#8AE234"),
  color_palette12_("color_palette12", "#FCE94F"),
  color_palette13_("color_palette13", "#729FCF"),
  color_palette14_("color_palette14", "#AD7FA8"),
  color_palette15_("color_palette15", "#34E2E2"),
  color_palette16_("color_palette16", "#EEEEEC")
{
  add(columns_);
  add(rows_);

  add(color_foreground_);
  add(color_background_);
  add(color_cursor_);
  add(color_highlight_);

  add(color_palette1_);
  add(color_palette2_);
  add(color_palette3_);
  add(color_palette4_);
  add(color_palette5_);
  add(color_palette6_);
  add(color_palette7_);
  add(color_palette8_);
  add(color_palette9_);
  add(color_palette10_);
  add(color_palette11_);
  add(color_palette12_);
  add(color_palette13_);
  add(color_palette14_);
  add(color_palette15_);
  add(color_palette16_);
}

//========================
// ConfMain::Alsa
ConfMain::Alsa::Alsa() :
  conf::Element("alsa"),
  card_        ("card",     ""),
  dev_         ("dev",      ""),
  buf_size_    ("buf_size", "")
{
  add(card_);
  add(dev_);
  add(buf_size_);
}

//========================
// ConfMain::Pulse
ConfMain::Pulse::Pulse() :
  conf::Element("pulse"),
  server_      ("server", ""),
  sink_        ("sink",   "")
{
  add(server_);
  add(sink_);
}

//========================
// ConfMain::Esd
ConfMain::Esd::Esd() :
  conf::Element("esd"),
  host_        ("host", "")
{
  add(host_);
}

//========================
// ConfMain::Oss
ConfMain::Oss::Oss() :
  conf::Element("oss"),
  dsp_         ("dsp", "")
{
  add(dsp_);
}

//========================
// ConfMain::Sound
ConfMain::Sound::Sound() :
  conf::Element("sound"),
  driver_      ("driver", ""),
  mute_        ("mute", false),
  alsa_        (new Alsa()),
  pulse_       (new Pulse()),
  esd_         (new Esd()),
  oss_         (new Oss())
{
  add(driver_);
  add(mute_);
  add(alsa_);
  add(pulse_);
  add(esd_);
  add(oss_);
}

//========================
// ConfMain::Migemo
ConfMain::Migemo::Migemo() :
  conf::Element  ("migemo"),
  mode_          ("mode",           MIGEMO_MODE_DISABLED),
  encoding_      ("encoding",       "EUC-JP"),
  min_length_    ("min_length",     2),
  dict_path_     ("dict_path",      "/usr/local/share/migemo/euc-jp/migemo-dict"),
  dict_path_user_("dict_path_user", ""),
  url_           ("url",            "http://127.0.0.1/migemo")
{
  add(mode_);
  add(encoding_);
  add(min_length_);
  add(dict_path_);
  add(dict_path_user_);
  add(url_);
}

//========================
// ConfMain
const char *ConfMain::FILE_NAME = "main.conf";

bool ConfMain::chk_numeric_command_args(RefCommand           command,
                                        const Glib::ustring &replacement) {
  bool res = false;
  Glib::ustring value_o = *command->command(), value_n;
  const char* l = value_o.c_str();
  for(const char* c = l; *c; ++c) {
    switch(*c) {
    case '%':
    case '#':
      if (c[1] == '{') {
        value_n.append(l, c);
        for(l = c + 2; *l && *l != '}'; ++l) ;
        Glib::ustring key(c, l + 1);
        Glib::ustring name(c + 2, l);
        if (toint(name, -1) > 0 || name == "*") {
          res = true;
          if (!replacement.empty()) {
            key = Glib::ustring(c, 2) + replacement + "}";
          }
        }
        value_n += key;
        c = l++;
      }
    }
  }
  value_n.append(l);
  if (res) command->command() = value_n;
  return res;
}

void ConfMain::on_postload() {
  conf::Document::on_postload();
  if (version() < "3.1") {
    bool r = false;
    if (chk_numeric_command_args(command_->terminal(),   "command")) r = true;
    if (chk_numeric_command_args(command_->webbrowser(), "url"))     r = true;
    if (chk_numeric_command_args(command_->bbsbrowser(), "url"))     r = true;
    if (r) {
      static DialogWrapper<WarningDialog> d;
      if (!d) {
        d.reset(new WarningDialog(_("Variable #{*} and #{1...} were abolished. Please check preference and changelog.")));
      }
      d->show();
    }
  }
}

ConfMain::ConfMain(const Glib::ustring &dir_path) :
  Document      (dir_path, FILE_NAME),
  dir_path_     (dir_path),
  general_      (new General()),
  screensaver_  (new ScreenSaver()),
  network_      (new Network()),
  command_      (new BuiltInCommand(dir_path_)),
  record_       (new Record()),
  preset_colors_(new PresetColors("preset_colors")),
  terminal_     (new Terminal()),
  sound_        (new Sound()),
  migemo_       (new Migemo()),
  text_editor_  (new TextEditor()),
  config_view_  (new ConfigView())
{
  add(general_);
  add(screensaver_);
  add(network_);
  add(command_);
  add(record_);
  add(preset_colors_);
  add(terminal_);
  add(sound_);
  add(migemo_);
  add(text_editor_);
  add(config_view_);
}

//========================
// ConfMenu::AttributeSPType
ConfMenu::AttributeSPType::NameTable
ConfMenu::AttributeSPType::_name_table[] = {
  {SPECIAL_TYPE_PLAYWITH, "play_with"},
  {SPECIAL_TYPE_INVALID,  NULL},
};

//========================
// ConfMenu::Menu
const char *ConfMenu::Menu::NAME_COMMAND   = "item";
const char *ConfMenu::Menu::NAME_SEPARATOR = "separator";
const char *ConfMenu::Menu::NAME_SPECIAL   = "special";

void ConfMenu::Menu::on_start_element(conf::ConfParser &parser,
                                      const char  *element_name,
                                      const char **attribute_names,
                                      const char **attribute_values,
                                      GError     **error) {
  const char **attribute_names_  = attribute_names;
  const char **attribute_values_ = attribute_values;

  if (parser.get_document().version() < "3.7") {
    struct Replace {
      const char *icon;
      const char *rep;
    };
    static const Replace icon_reps[] = {
      {"add",         GTK_STOCK_ADD},
      {"copy",        GTK_STOCK_COPY},
      {"home",        GTK_STOCK_HOME},
      {"open",        GTK_STOCK_OPEN},
      {"play",        GTK_STOCK_MEDIA_PLAY},
      {"preferences", GTK_STOCK_PREFERENCES},
      {"property",    GTK_STOCK_PROPERTIES},
      {"quit",        GTK_STOCK_QUIT},
      {"record",      GTK_STOCK_MEDIA_RECORD},
      {"refresh",     GTK_STOCK_REFRESH},
      {"remove",      GTK_STOCK_REMOVE},
      {"stop",        GTK_STOCK_STOP},
      {"error",       GTK_STOCK_DIALOG_ERROR},
      {"information", GTK_STOCK_INFO},
      {"warning",     GTK_STOCK_DIALOG_WARNING},
      {"clear",       GTK_STOCK_CLEAR},
      {"webbrowser",  IconManager::SPECIAL_WEBBROWSER},
      {"bbsbrowser",  IconManager::SPECIAL_BBSBROWSER},
      {NULL,          NULL}
    };

    if (get_element_depth() == 0 && strcmp(element_name, "item") == 0) {
      attribute_values_ =
        (const gchar **)g_strdupv((gchar **)attribute_values);

      const char **name = attribute_names_, **value = attribute_values_;
      for(; *name && *value; ++name, ++value) {
        if (strcmp(*name, "label") == 0) {
          if (*value && (*value)[0] == '\0') {
            element_name = NAME_SEPARATOR;
            break;
          }
        } else if (strcmp(*name, "icon") == 0) {
          for(const Replace *rep = icon_reps; rep->icon; ++rep) {
            if (strcmp(*value, rep->icon) == 0) {
              g_free((void *)*value);
              *value = g_strdup(rep->rep);
              break;
            }
          }
        }
      }
    }
  }
  if (parser.get_document().version() < "3.8") {
    attribute_values_ = (const gchar **)g_strdupv((gchar **)attribute_values);
    attribute_names_ = (const gchar **)g_strdupv((gchar **)attribute_names);
    if (strcmp(element_name, "item") == 0) {
      const char **name = attribute_names_, **value = attribute_values_;
      const char **label = NULL;
      bool r = false;
      for(; *name && *value; ++name, ++value) {
        if (strcmp(*name, "label") == 0) {
          label = value;
        } else if (strcmp(*name, "command") == 0 &&
                   strcmp(*value, "@{play2}") == 0) {
          element_name = "special";
          g_free((char *)*name);
          g_free((char *)*value);
          *name  = g_strdup("sptype");
          *value = g_strdup("play_with");
          r = true;
        }
      }
      if (r && label) {
        g_free((char *)*label);
        *label = g_strdup("Play _with ...");
      }
    }
  }
  conf::Collection<Menu, Menu>::on_start_element(parser,
                                                 element_name,
                                                 attribute_names_,
                                                 attribute_values_,
                                                 error);
  if (attribute_names_ != attribute_names) {
    g_strfreev((gchar **)attribute_names_);
  }
  if (attribute_values_ != attribute_values) {
    g_strfreev((gchar **)attribute_values_);
  }
}

void ConfMenu::Menu::on_add_item(RefItem item) {
  if (type_ == ITEM_TYPE_COMMAND || type_ == ITEM_TYPE_ROOT) {
    conf::Collection<Menu, Menu>::on_add_item(item);
  }
}
ConfMenu::RefMenu
ConfMenu::Menu::on_create_item(const Glib::ustring &item_name) {
  Glib::ustring name = item_name;
  ItemType type = ITEM_TYPE_COMMAND;
  if (item_name == NAME_SEPARATOR) {
    type = ITEM_TYPE_SEPARATOR;
  } else if (item_name == NAME_SPECIAL) {
    type = ITEM_TYPE_SPECIAL;
  } else {
    name = NAME_COMMAND;
  }
  return RefMenu(new Menu(name, type));
}

ConfMenu::RefMenu ConfMenu::Menu::add_item() {
  return conf::Collection<Menu, Menu>::add_item();
}
ConfMenu::RefMenu ConfMenu::Menu::add_item(const Glib::ustring &label,
                                           const Glib::ustring &command,
                                           const Glib::ustring &icon) {
  RefMenu item    = add_item();
  item->icon()    = icon;
  item->label()   = label;
  item->command() = command;
  return item;
}
ConfMenu::RefMenu ConfMenu::Menu::add_separator() {
  RefItem item = on_create_item(NAME_SEPARATOR);
  on_add_item(item);
  return item;
}
ConfMenu::RefMenu ConfMenu::Menu::add_special(const Glib::ustring &label,
                                              SpecialType          sptype,
                                              const Glib::ustring &icon) {
  RefItem item   = on_create_item(NAME_SPECIAL);
  item->icon()   = icon;
  item->label()  = label;
  item->sptype() = sptype;
  on_add_item(item);
  return item;
}

ConfMenu::Menu::Menu(const Glib::ustring &name, ItemType type) :
  conf::Collection<Menu, Menu>(name, Glib::ustring()),
  type_   (type),
  icon_   ("icon",    ""),
  label_  ("label",   ""),
  command_("command", ""),
  sptype_ ("sptype",  SPECIAL_TYPE_INVALID)
{
  if (type_ != ITEM_TYPE_SEPARATOR) {
    add(icon_);
    add(label_);
    add(command_);
  }
  if (type_ == ITEM_TYPE_SPECIAL) {
    add(sptype_);
  }
}

//========================
// ConfMenu
const char *ConfMenu::FILE_NAME = "menu.conf";

bool ConfMenu::chk_uptest_command(RefMenu menu) {
  if ((*menu->command()).find("@{Uptest.yp4g}") != Glib::ustring::npos) {
    return true;
  }
  for(Menu::iterator iter = menu->begin();
      iter != menu->end(); ++iter) {
    if (chk_uptest_command(*iter)) return true;
  }
  return false;
}

void ConfMenu::on_postload() {
  conf::Document::on_postload();
  if (version() < "3.6.2") {
    bool r = false;
    const ElementList &elements = get_elements();
    for(ElementList::const_iterator iter = elements.begin();
        iter != elements.end(); ++iter) {
      RefMenu menu = boost::dynamic_pointer_cast<Menu>(*iter);
      if (menu && chk_uptest_command(menu)) r = true;
    }
    if (r) {
      static DialogWrapper<WarningDialog> d;
      if (!d) {
        d.reset(new WarningDialog(_("Uptest plugin was merged to gpyp. Use @{uptest} instead of @{Uptest.yp4g}.")));
      }
      d->show();
    }
  }
}

ConfMenu::ConfMenu(const Glib::ustring &dir_path) :
  Document      (dir_path, FILE_NAME),
  yp_           (new Menu("ypmenu",        ITEM_TYPE_ROOT)),
  ch_           (new Menu("chmenu",        ITEM_TYPE_ROOT)),
  tool_         (new Menu("toolmenu",      ITEM_TYPE_ROOT)),
  text_         (new Menu("textmenu",      ITEM_TYPE_ROOT)),
  proc_         (new Menu("procmenu",      ITEM_TYPE_ROOT)),
  pc_relay_     (new Menu("pc_relay",      ITEM_TYPE_ROOT)),
  pc_connection_(new Menu("pc_connection", ITEM_TYPE_ROOT)),
  webview_      (new Menu("webviewmenu",   ITEM_TYPE_ROOT))
{
  add(yp_);
  add(ch_);
  add(tool_);
  add(text_);
  add(proc_);
  add(pc_relay_);
  add(pc_connection_);
  add(webview_);
}

//========================
// ConfFavorite
const char *ConfFavorite::FILE_NAME = "favorite.conf";

ConfFavorite::ConfFavorite(const Glib::ustring &dir_path) :
  Document(dir_path, FILE_NAME)
{
}

//========================
// ConfInput::InputMouse
ConfInput::InputMouse::InputMouse(const Glib::ustring &double_click,
                                  const Glib::ustring &button2) :
  conf::Element("mouse"),
  double_click_("double_click", double_click),
  button2_     ("button2",      button2),
  button6_     ("button6",      ""),
  button7_     ("button7",      ""),
  button8_     ("button8",      ""),
  button9_     ("button9",      ""),
  button10_    ("button10",     ""),
  button11_    ("button11",     ""),
  button12_    ("button12",     ""),
  button13_    ("button13",     ""),
  button14_    ("button14",     ""),
  button15_    ("button15",     ""),
  button16_    ("button16",     ""),
  button17_    ("button17",     ""),
  button18_    ("button18",     ""),
  button19_    ("button19",     ""),
  button20_    ("button20",     "")
{
  add(double_click_);
  add(button2_);
  add(button6_);
  add(button7_);
  add(button8_);
  add(button9_);
  add(button10_);
  add(button11_);
  add(button12_);
  add(button13_);
  add(button14_);
  add(button15_);
  add(button16_);
  add(button17_);
  add(button18_);
  add(button19_);
  add(button20_);
}

//========================
// ConfInput::InputEntry
ConfInput::InputEntry::InputEntry(const Glib::ustring &name,
                                  InputMouse          *mouse,
                                  KeyBind             *key) :
  conf::Element(name),
  mouse_       (mouse),
  key_         (key)
{
  if (mouse_) add(mouse_);
  if (key_)   add(key_);
}

//========================
// ConfInput::OldKeyProxy
void ConfInput::OldKeyProxy::on_start_element(conf::ConfParser &parser,
                                              const char  *element_name,
                                              const char **attribute_names,
                                              const char **attribute_values,
                                              GError     **error) {
  if (get_element_depth() == 0) {
    if (!strcmp(element_name, "mainwindow")) {
      parser.push(input_.mainwindow()->key().get(), element_name,
                  attribute_names, attribute_values);
      return;
    } else if (!strcmp(element_name, "searchbar")) {
      parser.push(input_.searchbar()->key().get(), element_name,
                  attribute_names, attribute_values);
      return;
    } else if (!strcmp(element_name, "channellist")) {
      parser.push(input_.channellist()->key().get(), element_name,
                  attribute_names, attribute_values);
      return;
    }
  }
  Element::on_start_element(parser, element_name,
                            attribute_names, attribute_values,
                            error);
}

ConfInput::OldKeyProxy::OldKeyProxy(ConfInput &input) :
  conf::Element("key"),
  input_       (input)
{
}

//========================
// ConfInput
const char *ConfInput::FILE_NAME = "input.conf";

void ConfInput::on_start_element(conf::ConfParser &parser,
                                 const char  *element_name,
                                 const char **attribute_names,
                                 const char **attribute_values,
                                 GError     **error) {
  if (get_element_depth() == 0) {
    if (parser.get_document().version() < "3.3") {
      if (!strcmp(element_name, "mouse")) {
        parser.push(channellist_->mouse().get(), element_name,
                    attribute_names, attribute_values);
        return;
      } else if (!strcmp(element_name, "key")) {
        old_key_proxy_.reset(new OldKeyProxy(*this));
        parser.push(old_key_proxy_.get(), element_name,
                    attribute_names, attribute_values);
        return;
      }
    }
    if (parser.get_document().version() < "3.4") {
      if (!strcmp(element_name, "children")) {
        parser.push(process_.get(), element_name,
                    attribute_names, attribute_values);
        return;
      }
    }
  }
  Element::on_start_element(parser, element_name,
                            attribute_names, attribute_values,
                            error);
}
void ConfInput::on_postload() {
  old_key_proxy_.reset();
  conf::Document::on_postload();
}

ConfInput::ConfInput(const Glib::ustring &dir_path) :
  Document      (dir_path, FILE_NAME),
  mainwindow_   (new InputEntry("mainwindow", NULL, new KeyBind())),
  channellist_  (new InputEntry("channellist",
                                new InputMouse("@{play}",
                                               "@{copy} \"#{Channel.streamurl}\""),
                                new KeyBind())),
  process_      (new InputEntry("process",
                                new InputMouse("@{Process.kill}",
                                               "@{copy} \"#{Process.pid}\""),
                                new KeyBind())),
  peercast_     (new InputEntry("peercast", NULL, new KeyBind())),
  pc_relay_     (new InputEntry("pc_relay",
                                new InputMouse("@{PeerCast.Relay.disconnect}",
                                               "@{play}"),
                                new KeyBind())),
  pc_connection_(new InputEntry("pc_connection",
                                new InputMouse("@{PeerCast.Connection.disconnect}"),
                                new KeyBind())),
  text_editor_  (new InputEntry("texteditor", NULL, new KeyBind())),
  search_       (new InputEntry("search",     NULL, new KeyBind())),
  searchbar_    (new InputEntry("searchbar",  NULL, new KeyBind())),
  webview_      (new InputEntry("webview",    new InputMouse(), new KeyBind()))
{
  add(mainwindow_);
  add(channellist_);
  add(process_);
  add(peercast_);
  add(pc_relay_);
  add(pc_connection_);
  add(text_editor_);
  add(search_);
  add(searchbar_);
  add(webview_);

  pc_relay_->key()->append_child(peercast_->key());
  pc_connection_->key()->append_child(peercast_->key());

  searchbar_->key()->append_child(search_->key());
}

//========================
// Conf
void Conf::load(bool load) {
  if (load) {
    main_    ->load();
    menu_    ->load();
    favorite_->load();
    input_   ->load();
  } else {
    main_    ->load_default();
    menu_    ->load_default();
    favorite_->load_default();
    input_   ->load_default();
  }

  loaded_ = true;
  signal_loaded_();
}
void Conf::save() {
  log::dbg << "Conf::save" << log::endl;
  try {
    std::string dir = Glib::filename_from_utf8(dir_path_);
    if (!Glib::file_test(dir, Glib::FILE_TEST_EXISTS) &&
        g_mkdir_with_parents(dir.c_str(), 0755)) {
      log::err << format(_("failed to create directory - \"%s\""),
                         dir_path_.c_str())
               << log::endl;
    }
    if (!Glib::file_test(dir, Glib::FILE_TEST_IS_DIR)) {
      log::err << format(_("\"%s\" is not directory"), dir_path_.c_str())
               << log::endl;
      return;
    }
  } catch(const Glib::Exception &ex) {
    log::err << "Conf: " << ex.what() << log::endl;
    return;
  }
  main_    ->save();
  menu_    ->save();
  favorite_->save();
  input_   ->save();
  log::log << _("Configuration saved") << log::endl;
}

Conf::Conf(const Glib::ustring &dir_path) :
  dir_path_ (dir_path),
  main_     (new ConfMain    (dir_path_)),
  menu_     (new ConfMenu    (dir_path_)),
  favorite_ (new ConfFavorite(dir_path_)),
  input_    (new ConfInput   (dir_path_)),
  loaded_   (false)
{
}

}/*gpyp*/
